<?php

namespace App\Services;

use RuntimeException;
use Illuminate\Encryption\Encrypter;
use Illuminate\Support\Str;

class CryptService
{
    // 定义加密参数
    private $key;

    // 定义加密方式
    private $cipher;

    // 单例函数的实例化
    private static $instance;

    public function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    // 定义初始化
    private function __construct()
    {
        // If the key starts with "base64:", we will need to decode the key before handing
        // it off to the encrypter. Keys may be base-64 encoded for presentation and we
        // want to make sure to convert them back to the raw bytes before encrypting.
        // 如果没有传key的值，可以使用默认的key值
        $this->default();
    }

    /*
     * 设置默认的属性
     * 本类是一个单例模式的类，如果前期设置了配置，后期又想使用默认配置，你需要调用default方法才行
     */
    public function default()
    {
        $this->key = config('app.key');
        $this->cipher = config('app.cipher');
        return $this;
    }

    // 设置key的值
    public function setKey($key)
    {
        $this->key = $key;
        return $this;
    }

    // 设置cipher的值
    public function setCipher($cipher)
    {
        $this->cipher = $cipher;
        return $this;
    }

    /*
     * 定义公开的加密函数
     */
    public function encrypt($value, $serialize = true)
    {
        // 处理之前进行一步验证
        return $this->getEncrypter()->encrypt($value, $serialize);
    }

    /*
     * 直接加密
     */
    public function encryptString($value)
    {
        return $this->getEncrypter()->encrypt($value, false);
    }

    /*
     * 定义公开的解密函数
     */
    public function decrypt($payload, $unserialize = true)
    {
        // 处理之前进行一步验证
        return $this->getEncrypter()->decrypt($payload, $unserialize);
    }

    /*
     * 直接解密
     */
    public function decryptString($payload)
    {
        return $this->getEncrypter()->decrypt($payload, false);
    }

    /**
     * Determine if the given key and cipher combination is valid.
     *
     * @param string $key
     * @param string $cipher
     * @return bool
     */
    private function supported($key, $cipher)
    {
        $length = mb_strlen($key, '8bit');

        return ($cipher === 'AES-128-CBC' && $length === 16) ||
            ($cipher === 'AES-256-CBC' && $length === 32);
    }

    /*
     * 加密之前的验证
     */
    private function getEncrypter()
    {
        if (Str::startsWith($this->key, 'base64:')) {
            $this->key = base64_decode(substr($this->key, 7));
        }
        $this->key = (string)$this->key;
        if (!static::supported($this->key, $this->cipher)) {
            throw new RuntimeException('The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths.');
        }

        return new Encrypter($this->key, $this->cipher);
    }
}
